from conf.my_log import MyLog
from conf.neo4j_cnx import NeoCnxPool

logger = MyLog().get_logger()

neo_cnx = NeoCnxPool()


class AlterLabel:

    @staticmethod
    def find_all_child(uid):
        uid_list = []

        def tranverse_child(uid):
            with neo_cnx.get_session() as session:
                query = (
                    'MATCH (a:ICD11 {uid: $uid}) '
                    'MATCH (a)-[:Child]->(b) '
                    'RETURN b.uid as uid'
                )
                results = session.run(query, uid=uid)
                results = [record['uid'] for record in results.data()]
                for ch_uid in results:
                    uid_list.append(ch_uid)
                    tranverse_child(ch_uid)
                return results

        tranverse_child(uid)

        return uid_list

    @staticmethod
    def find_all_parent(uid):
        uid_list = []

        def tranverse_parent(uid):
            with neo_cnx.get_session() as session:
                query = (
                    'MATCH (a:ICD11 {uid: $uid}) '
                    'MATCH (a)-[:Parent]->(b) '
                    'RETURN b '
                    'LIMIT 1'
                )
                results = session.run(query, uid=uid)
                results = [record['b'] for record in results.data()]
                if len(results) > 0:
                    uid_list.append(results[0]['uid'])
                    tranverse_parent(results[0]['uid'])

        tranverse_parent(uid)
        return uid_list

    @staticmethod
    def find_child_uids(uid):
        with neo_cnx.get_session() as session:
            query = (
                'MATCH (a:ICD11 {uid: $uid}) '
                'RETURN a.child as child '
            )
            results = session.run(query, uid=uid)
            results = [record['child'] for record in results.data()]
            results = results[0]
            return results

    @staticmethod
    def query_labels(uid):
        with neo_cnx.get_session() as session:
            query = (
                'MATCH (n:ICD11 {uid: $uid}) '
                'RETURN labels(n) as label'
            )
            results = session.run(query, uid=uid)
            results = [record['label'] for record in results.data()][0]
            return results

    @staticmethod
    def remove_labels(uid, label_list):
        logger.info(f'Removing parent labels, uid is {uid}, label_set is {label_list}')
        with neo_cnx.get_session() as session:
            for label in label_list:
                query = (
                            'MATCH (n:ICD11 {uid: $uid}) '
                            'REMOVE n:%s'
                        ) % label
                session.run(query, uid=uid)

    @staticmethod
    def add_labels(uid, label_list):
        logger.info(f'Adding labels, uid is {uid}, new_label is {label_list}')
        with neo_cnx.get_session() as session:
            for label in label_list:
                # Adding New Label To The Node
                query = (
                            'MATCH (n:ICD11 {uid: $uid}) '
                            'SET n:%s'
                        ) % label
                session.run(query, uid=uid)

    def auto_alter(self, uid):
        logger.info("Change all parent's label")
        parent_uid_list = self.find_all_parent(uid)
        for parent_uid in parent_uid_list:
            # Get direct child labels to alter parent's labels
            child_label_list = []
            child_uid_list = self.find_child_uids(parent_uid)
            for ch_uid in child_uid_list:
                label_list = self.query_labels(ch_uid)
                child_label_list.extend(label_list)
            parent_label_list = self.query_labels(parent_uid)

            del_label_set = set(parent_label_list) - set(child_label_list)
            self.remove_labels(parent_uid, del_label_set)

            add_label_set = set(child_label_list) - set(parent_label_list)
            self.add_labels(parent_uid, add_label_set)

    def alter_label(self, uid, new_label):
        label_list = self.query_labels(uid)
        label_list.remove('ICD11')
        if label_list == [new_label]:
            return

        logger.info("First, Change all child's label")
        uid_list = self.find_all_child(uid)
        uid_list.append(uid)
        for ch_uid in uid_list:
            label_list = self.query_labels(uid=ch_uid)
            label_list.remove('ICD11')
            if label_list == [new_label]:
                continue
            self.remove_labels(ch_uid, label_list)
            self.add_labels(ch_uid, [new_label])

        logger.info("Second, Change all parent's label")
        parent_uid_list = self.find_all_parent(uid)
        for parent_uid in parent_uid_list:
            # Get direct child labels to alter parent's labels
            child_label_list = []
            child_uid_list = self.find_child_uids(parent_uid)
            for ch_uid in child_uid_list:
                label_list = self.query_labels(ch_uid)
                child_label_list.extend(label_list)
            parent_label_list = self.query_labels(parent_uid)

            del_label_set = set(parent_label_list) - set(child_label_list)
            self.remove_labels(parent_uid, del_label_set)

            add_label_set = set(child_label_list) - set(parent_label_list)
            self.add_labels(parent_uid, add_label_set)


if __name__ == '__main__':
    alter_label_app = AlterLabel()
    alter_label_app.find_all_child(uid='1834648119')
    alter_label_app.find_all_parent(uid='1834648119')

    alter_label_app.alter_label(uid='1435254666', new_label='疾病')

    neo_cnx.close()
